#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <dlfcn.h>

#include "iniparser.h"
#include "ini-wrapper.h"

//invalid value of every data type
#define STRING_NOT_FOUND_VALUE  (NULL)
#define INT_NOT_FOUND_VALUE (0xffabcdef)
#define LONGINT_NOT_FOUND_VALUE (0xabcdefffffabcdef)
#define BOOL_NOT_FOUND_VALUE    (-1)
#define DOUBLE_NOT_FOUND_VALUE  (123.123)



static int32_t member_parse(const dictionary *d, const int8_t *section_name, struct member_info *member)
{
    int8_t member_total_name[SECTION_NAME_SIZE + MEMBER_NAME_SIZE + 2] = {0};  //':' 和 '\0' 各占一字节
    int32_t ret = 0;
    const int8_t *s = NULL;

    sprintf(member_total_name, "%s:%s", section_name, member->name);

    switch (member->type)
    {
        case TYPE_STRING:
            s = iniparser_getstring(d, member_total_name, STRING_NOT_FOUND_VALUE);
            if (STRING_NOT_FOUND_VALUE == s)
            {
                printf("can not get this member value:%s\n", member->name);
                return -1;
            }
            snprintf(member->v.s, sizeof(member->v.s), "%s", s);
            break;
        case TYPE_INT:
            member->v.i32 = iniparser_getint(d, member_total_name, INT_NOT_FOUND_VALUE);
            if (INT_NOT_FOUND_VALUE == member->v.i32)
            {
                printf("can not get this member value:%s\n", member->name);
                return -1;
            }
            break;
        case TYPE_LONG:
            member->v.i64 = iniparser_getlongint(d, member_total_name, LONGINT_NOT_FOUND_VALUE);
            if (INT_NOT_FOUND_VALUE == member->v.i64)
            {
                printf("can not get this member value:%s\n", member->name);
                return -1;
            }
            break;
        case TYPE_BOOL:
            ret = iniparser_getboolean(d, member_total_name, BOOL_NOT_FOUND_VALUE);
            if (BOOL_NOT_FOUND_VALUE == ret)
            {
                printf("can not get this member value:%s\n", member->name);
                return -1;
            }
            member->v.i8 = ret;
            break;
        case TYPE_DOUBLE:
            member->v.d64 = iniparser_getdouble(d, member_total_name, DOUBLE_NOT_FOUND_VALUE);
            if (0.000001 >= abs(member->v.d64 - DOUBLE_NOT_FOUND_VALUE))
            {
                printf("can not get this member value:%s\n", member->name);
                return -1;
            }
            break;
        default:
            printf("do not support this data type:%d\n", member->type);
            return -1;
    }

    return 0;
}

static int32_t member_set(dictionary *d, const int8_t *section_name, struct member_info *member)
{
    int8_t member_total_name[SECTION_NAME_SIZE + MEMBER_NAME_SIZE + 2] = {0};  //plus 2:1 for ':', the other for '\0'
    int32_t ret = 0;
    int8_t val_str[32] = {0};
    const int8_t *s = NULL;

    sprintf(member_total_name, "%s:%s", section_name, member->name);

    ret = iniparser_find_entry(d, member_total_name);
    if (0 == ret)
    {
        printf("member is not existed, add it to ini file\n");
    }

    switch (member->type)
    {
        case TYPE_STRING:
            ret = iniparser_set(d, member_total_name, member->v.s);
            if (0 != ret)
            {
                printf("can not set this member value:%s\n", member->name);
                return -1;
            }
            break;
        case TYPE_INT:
            snprintf(val_str, sizeof(val_str), "%d", member->v.i32);
            ret = iniparser_set(d, member_total_name, val_str);
            if (0 != ret)
            {
                printf("can not set this member value:%s\n", member->name);
                return -1;
            }
            break;
        case TYPE_LONG:
            snprintf(val_str, sizeof(val_str), "%ld", member->v.i64);
            ret = iniparser_set(d, member_total_name, val_str);
            if (0 != ret)
            {
                printf("can not set this member value:%s\n", member->name);
                return -1;
            }
            break;
        case TYPE_BOOL:
            snprintf(val_str, sizeof(val_str), "%d", member->v.i8);
            ret = iniparser_set(d, member_total_name, val_str);
            if (0 != ret)
            {
                printf("can not set this member value:%s\n", member->name);
                return -1;
            }
            break;
        case TYPE_DOUBLE:
            snprintf(val_str, sizeof(val_str), "%lf", member->v.d64);
            ret = iniparser_set(d, member_total_name, val_str);
            if (0 != ret)
            {
                printf("can not set this member value:%s\n", member->name);
                return -1;
            }
            break;
        default:
            printf("do not support this data type:%d\n", member->type);
            return -1;
    }

    return 0;
}

static int32_t section_parse(const dictionary *d, struct section_info *section)
{
    int32_t i = 0;
    int32_t ret = 0;
    const int8_t *section_name = section->base.name;
    struct member_info *member_ptr = (struct member_info *)((uint8_t *)section + sizeof(struct section_info));

    for (i = 0; i < section->base.member_num; i++)
    {
        ret = member_parse(d, section_name, member_ptr);
        if (0 != ret)
        {
            printf("member '%s' of section '%s' parse failed\n", member_ptr->name, section_name);
            return -1;
        }
        member_ptr++;
    }

    return 0;
}

static int32_t section_set(dictionary *d, struct section_info *section)
{
    int32_t i = 0;
    int32_t ret = 0;
    const int8_t *section_name = section->base.name;
    struct member_info *member_ptr = (struct member_info *)((uint8_t *)section + sizeof(struct section_info));

    for (i = 0; i < section->base.member_num; i++)
    {
        ret = member_set(d, section_name, member_ptr);
        if (0 != ret)
        {
            printf("member '%s' of section '%s' parse failed\n", member_ptr->name, section_name);
            return -1;
        }
        member_ptr++;
    }

    return 0;
}

int32_t inifile_config_parse(struct ini_config *config)
{
    int32_t ret = 0;
    dictionary *d = NULL;
    struct section_info *valid_section_ptr = NULL;
    int32_t sections_num_of_file = 0;
    const int8_t *section_name = NULL;
    int32_t i = 0;
    int32_t j = 0;

    if (NULL == config)
    {
        printf("invalid parameter\n");
        return -1;
    }

    d = iniparser_load(config->ini.filename);
    if (NULL == d)
    {
        printf("parse %s failed\n", config->ini.filename);
        return -1;
    }

    sections_num_of_file = iniparser_getnsec(d);
    if (sections_num_of_file != config->ini.section_num)
    {
        printf("section num in ini file(%d) is not match your configuration(%ld)\n", sections_num_of_file, config->ini.section_num);
        goto exit;
    }

//  iniparser_dump(d, stdout);

    valid_section_ptr = (struct section_info *)((uint8_t *)config + sizeof(struct ini_info));

    //检测 section name 是否合法
    for (i = 0; i < sections_num_of_file; i++)
    {
        section_name = iniparser_getsecname(d, i);
        for (j = 0; j < sections_num_of_file; j++)
        {
            if (0 == memcmp(section_name, valid_section_ptr->base.name, strlen(section_name)))
            {
                break;
            }

            valid_section_ptr = (struct section_info *)((uint8_t *)valid_section_ptr + sizeof(struct section_info) + valid_section_ptr->base.member_num * sizeof(struct member_info));
        }

        if (j >= sections_num_of_file)
        {
            printf("invalid secton name:%s\n", section_name);
            goto exit;
        }

        valid_section_ptr = (struct section_info *)((uint8_t *)config + sizeof(struct ini_info));
    }
    //解析 section
    valid_section_ptr = (struct section_info *)((uint8_t *)config + sizeof(struct ini_info));
    for (i = 0; i < sections_num_of_file; i++)
    {
        ret = section_parse(d, valid_section_ptr);
        if (0 != ret)
        {
            printf("parse section %s failed\n", valid_section_ptr->base.name);
            goto exit;
        }

        valid_section_ptr = (struct section_info *)((uint8_t *)valid_section_ptr + sizeof(struct section_info) + valid_section_ptr->base.member_num * sizeof(struct member_info));
    }

    iniparser_freedict(d);
    return 0;
exit:

    iniparser_freedict(d);
    return -1;
}

int32_t inifile_config_save(struct ini_config *config)
{
    int32_t ret = 0;
    dictionary *d = NULL;
    FILE *fp = NULL;
    int32_t i = 0;
    struct section_info *valid_section_ptr = NULL;

    if (NULL == config)
    {
        printf("invalid parameter\n");
        return -1;
    }

    d = iniparser_load(config->ini.filename);
    if (NULL == d)
    {
        printf("parse %s failed\n", config->ini.filename);
        return -1;
    }

//  iniparser_dump(d, stdout);

    valid_section_ptr = (struct section_info *)((uint8_t *)config + sizeof(struct ini_info));

    for (i = 0; i < config->ini.section_num; i++)
    {
        ret = section_set(d, valid_section_ptr);
        if (0 != ret)
        {
            printf("set section %s failed\n", valid_section_ptr->base.name);
            goto exit;
        }

        valid_section_ptr = (struct section_info *)((uint8_t *)valid_section_ptr + valid_section_ptr->base.member_num * sizeof(struct member_info) + sizeof(struct section_info));
    }

    fp = fopen(config->ini.filename, "w");
    if (NULL == fp)
    {
        printf("open %s failed\n", config->ini.filename);
        goto exit;
    }

//  iniparser_dump(d, stdout);
    iniparser_dump_ini(d, fp);

    fflush(fp);

    fclose(fp);

    sync();

    iniparser_freedict(d);

    return 0;
exit:

    iniparser_freedict(d);
    return -1;
}

